import openal
import wave
import os
import ctypes
from typing import Dict, List

class AudioManager:
    def __init__(self):
        """初始化音频设备和资源存储"""
        try:
            # 初始化设备
            self.device = openal.alcOpenDevice(None)
            if not self.device:
                raise RuntimeError("无法打开音频设备")

            # 创建上下文
            self.context = openal.alcCreateContext(self.device, None)
            if not self.context:
                raise RuntimeError("无法创建音频上下文")

            openal.alcMakeContextCurrent(self.context)

            # 存储资源
            self.sound_buffers: Dict[str, ctypes.c_uint] = {}
            self.active_sources: List[ctypes.c_uint] = []

            # 初始化监听器
            self.update_listener_position((0.0, 0.0, 0.0))

        except Exception as e:
            self._safe_release()
            raise RuntimeError(f"音频系统初始化失败: {str(e)}")

    def preload_sound(self, sound_id: str, filepath: str):
        """预加载音效"""
        if not os.path.exists(filepath):
            raise FileNotFoundError(f"音效文件不存在: {filepath}")

        if sound_id in self.sound_buffers:
            return

        try:
            with wave.open(filepath, 'rb') as wav_file:
                channels = wav_file.getnchannels()
                sample_width = wav_file.getsampwidth()
                sample_rate = wav_file.getframerate()
                pcm_data = wav_file.readframes(wav_file.getnframes())

            # 创建缓冲区
            buffer = ctypes.c_uint(0)
            openal.alGenBuffers(1, ctypes.byref(buffer))

            # 确定格式
            format_ = (openal.AL_FORMAT_MONO16 if channels == 1
                       else openal.AL_FORMAT_STEREO16)

            # 填充缓冲区数据
            openal.alBufferData(
                buffer,
                format_,
                pcm_data,
                len(pcm_data),
                sample_rate
            )

            self.sound_buffers[sound_id] = buffer
        except Exception as e:
            raise RuntimeError(f"加载音效失败 {filepath}: {str(e)}")

    def play_sound(self, sound_id: str, position=(0.0, 0.0, 0.0), volume=1.0, loop=False):
        """播放音效"""
        if sound_id not in self.sound_buffers:
            raise ValueError(f"音效未加载: {sound_id}")

        self._cleanup_finished_sources()

        # 创建声源
        source = ctypes.c_uint(0)
        openal.alGenSources(1, ctypes.byref(source))

        # 设置声源属性
        buffer_id = self.sound_buffers[sound_id].value
        openal.alSourcei(source, openal.AL_BUFFER, buffer_id)
        openal.alSource3f(source, openal.AL_POSITION, *position)
        openal.alSourcef(source, openal.AL_GAIN, volume)
        openal.alSourcei(source, openal.AL_LOOPING,
                         openal.AL_TRUE if loop else openal.AL_FALSE)

        # 播放
        openal.alSourcePlay(source)
        self.active_sources.append(source)
        return source

    def update_listener_position(self, position, forward=(0.0, 0.0, -1.0), up=(0.0, 1.0, 0.0)):
        """更新监听器位置"""
        # 使用ctypes正确传递浮点数组
        pos_array = (ctypes.c_float * 3)(*position)
        openal.alListenerfv(openal.AL_POSITION, pos_array)

        # 设置方向
        ori_array = (ctypes.c_float * 6)(*(forward + up))
        openal.alListenerfv(openal.AL_ORIENTATION, ori_array)

    def stop_all_sounds(self):
        """停止所有音效"""
        for source in self.active_sources:
            openal.alSourceStop(source)
            openal.alDeleteSources(1, ctypes.byref(source))
        self.active_sources.clear()

    def _cleanup_finished_sources(self):
        """清理完成音效"""
        remaining = []
        for source in self.active_sources:
            state = ctypes.c_int(0)
            openal.alGetSourcei(source, openal.AL_SOURCE_STATE, ctypes.byref(state))
            if state.value != openal.AL_PLAYING:
                openal.alDeleteSources(1, ctypes.byref(source))
            else:
                remaining.append(source)
        self.active_sources = remaining

    def _safe_release(self):
        """安全释放资源"""
        try:
            self.stop_all_sounds()
            for buffer in self.sound_buffers.values():
                openal.alDeleteBuffers(1, ctypes.byref(buffer))
            if hasattr(self, 'context'):
                openal.alcDestroyContext(self.context)
            if hasattr(self, 'device'):
                openal.alcCloseDevice(self.device)
        except:
            pass

    def release(self):
        """释放资源"""
        self._safe_release()

    def __del__(self):
        """析构函数"""
        self.release()

audio_player = AudioManager()
def audio_init():
    global audio_player
    try:
        # 预加载
        audio_player.preload_sound("dig.grass", "Resources/Sounds/grass2.wav")
        audio_player.preload_sound("dig.stone", "Resources/Sounds/stone4.wav")
        audio_player.preload_sound("dig.dirt", "Resources/Sounds/gravel1.wav")
        audio_player.preload_sound("dig.wood", "Resources/Sounds/wood3.wav")
        audio_player.preload_sound("dig.sand", "Resources/Sounds/sand1.wav")
        audio_player.preload_sound("button.click", "Resources/Sounds/click_stereo.wav")
        audio_player.preload_sound("screenshot", "Resources/Sounds/testaudio.wav")
    finally:
        # 确保释放
        if 'audio' in locals():
            audio_player.release()

